Skip to content

Reimplement _forEachField from the stdlib and use it (aka perverse runtime black magic)#397

Draft
kaascevich wants to merge 6 commits intomoreSwift:mainfrom
kaascevich:_forEachField
Draft

Reimplement _forEachField from the stdlib and use it (aka perverse runtime black magic)#397
kaascevich wants to merge 6 commits intomoreSwift:mainfrom
kaascevich:_forEachField

Conversation

@kaascevich
Copy link
Contributor

There's a function called _forEachField(of:options:body:) in the standard library, calls directly into the runtime to retrieve the names, types, and (importantly for us) offsets of a type's stored properties. Since it doesn't create any Mirror instances, it's (presumably) more performant than iterating over Mirror.children, and it's unaffected by CustomReflectable conformances.

This function would be perfect for updating dynamic properties, but there's a catch: _forEachField(of:options:body:) is annotated with @_spi(Reflection), effectively making it inaccessible in release toolchains. So what I did was copy the stdlib's implementation into SwiftCrossUI, then shrink it down to what we actually need. In the end, we only required three runtime functions:

@_silgen_name("swift_reflectionMirror_recursiveCount")
private func getRecursiveChildCount(of: Any.Type) -> Int

@_silgen_name("swift_reflectionMirror_recursiveChildMetadata")
private func getChildMetadata(
    of: Any.Type,
    index: Int,
    fieldMetadata: UnsafeMutablePointer<_FieldReflectionMetadata>
) -> Any.Type

@_silgen_name("swift_reflectionMirror_recursiveChildOffset")
private func getChildOffset(of: Any.Type, index: Int) -> Int

(_FieldReflectionMetadata comes from the SwiftShims module, which for reasons beyond my mortal comprehension is completely accessible and can be imported like any other module.)

Here's the Swift Forums thread @stackotter created discussing whether these functions are safe for us to use long-term -- we never got an official answer from the Swift team, but the conclusion reached by @tera was:

  • technically not
  • practically yes

Of course, benchmarking this to make sure that it's actually meaningfully faster than the current implementation will be a necessity.

@kaascevich kaascevich changed the title ~~Perverse runtime black magic~~ Reimplement _forEachField from the stdlib and use it Reimplement _forEachField from the stdlib and use it (aka perverse runtime black magic) Feb 3, 2026
@stackotter stackotter marked this pull request as draft February 4, 2026 03:00
@stackotter
Copy link
Collaborator

I'll leave this as a draft until we've done some benchmarking, because I probably won't merge the PR unless we have a compelling reason, due to the risks with using SPI runtime functions that may change at any time (afaict).

@kaascevich
Copy link
Contributor Author

That is fair and I absolutely agree with that.

@stackotter
Copy link
Collaborator

Alternatively, if we can get a guarantee that these runtime APIs are safe to use, then I would be happy to merge even if the performance gains are negligible, because it's a much nicer approach than my current Mirror-based performance hack.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants